# !/usr/bin/env python
# -*-coding:utf-8 -*-
"""
# File       : orderlines_plugin.py
# Time       ：2023/7/20 21:53
# Author     ：Y-aong
# version    ：python 3.7
# Description：
    orderlines插件工具，自动将插件中的函数信息写到数据库中
    The plug-in tool automatically writes the function information in the plug-in to the database
"""
import inspect
from typing import Any

from pydantic import BaseModel

from apis.plugin.models.plugin_info import PluginInfo
from orderlines.execute.running_check import CheckModule
from public.base_model import session_scope


class OrderlinesPlugHelper:

    def __init__(self):
        self.base_params = [
            'process_id',
            'process_name',
            'process_info',
            'task_nodes',
            'task_id',
            'task_config',
            'result',
            'process_instance_id'
        ]
        self.exclude_method = ['on_receive', 'on_success', 'on_failure']
        self.node_type = {
            'BuiltIn': 'base',
            'ProcessControl': 'process_control',
            'Group': 'group',
            'Parallel': 'parallel',
        }
        self.node_title = {
            'BuiltIn': '基础节点',
            'ProcessControl': '流程网关',
            'Group': '流程网关',
            'Parallel': '流程网关'
        }
        self.node_types = {
            'start': 'start-node',
            'end': 'end-node',
            'select': 'select-node',
            'parallel_task': 'parallel-node',
            'parallel_start': 'parallel-node',
            'process_control': 'process-control-node',
            'task_group': 'group-node',
            'sub_process': 'sub-process-node',
        }

    def init_plugin(self):
        """初始化插件信息"""
        modules = CheckModule().get_module()
        for key in modules.values():
            plugin = self.handle_orderlines_plugin(key)
            self.insert_info_plugin_info(plugin)

    def insert_info_plugin_info(self, plugin):
        """将插件信息写入数据库中"""
        methods = plugin.get('methods')
        with session_scope() as session:
            for method in methods:
                obj = session.query(PluginInfo).filter(
                    PluginInfo.class_name == plugin.get('class_name'),
                    PluginInfo.version == plugin.get('version'),
                    PluginInfo.method_name == method.get('method_name')
                ).first()

                plugin_info = {
                    'class_name': plugin.get('class_name'),
                    'category': plugin.get('category') or '其他任务',
                    'icon': plugin.get('icon') or 'LocationFilled',
                    'version': plugin.get('version'),
                    'method_name': method.get('method_name'),
                    'method_desc': method.get('method_desc'),
                    'parameters': method.get('parameters'),
                    'return_value': method.get('return'),
                    'plugin_title': plugin.get('title') or plugin.get('class_name'),
                }
                plugin_info.setdefault('node_type', self.node_types.get(method.get('method_name'), 'function-node'))
                plugin_info.setdefault('title', self.node_title.get(plugin.get('class_name'), plugin.get('title')))
                plugin_info.setdefault('background', 'rgb(255,255,255)')
                if obj:
                    session.query(PluginInfo).filter(
                        PluginInfo.class_name == plugin.get('class_name'),
                        PluginInfo.version == plugin.get('version'),
                        PluginInfo.method_name == method.get('method_name')).update(plugin_info)
                else:
                    obj = PluginInfo(**plugin_info)
                    session.add(obj)
                session.commit()

    def parse_pydantic_annotation(self, annotation: BaseModel, is_params=True):
        """解析pydantic类型的注解,Parse annotations of type pydantic"""
        field_info = list()
        properties = annotation.model_json_schema().get('properties')
        model_fields = annotation.model_fields
        for property_name, property_val in properties.items():
            if property_name != 'task_instance_id':
                description = property_val.get('description')
                annotation_info = {
                    'name': property_name,
                    'title': property_val.get('title'),
                    'type': str(model_fields.get(property_name).annotation),
                    'default': property_val.get('default'),
                    'required': False if property_val.get('default') else True,
                    'desc': description.split('-')[0],
                    # 参数的类型，输入框还是文件选择框或者是python代码
                    'param_type': description.split('-')[-1] if '-' in description else 'input'
                }
                if property_name not in self.base_params and is_params:
                    field_info.append(annotation_info)
                if not is_params:
                    field_info.append(annotation_info)

        return field_info

    @staticmethod
    def parse_common_annotation(params: Any):
        """解析普通类型的注解,Parse annotations of type common"""
        field_info = list()
        for name, param in dict(params).items():
            if name != 'self' and name != 'task_instance_id':
                default = '' if "empty" in str(param.default) else str(param.default)
                field_info.append({
                    'name': str(param.name),
                    'title': str(param.name).title(),
                    'type': '' if "empty" in str(param.annotation) else str(param.annotation),
                    'default': default,
                    'required': False if default else True,
                    'desc': ''
                })
        return field_info

    @staticmethod
    def handle_func_doc(func_doc: str) -> str:
        """处理函数的注释, Handles comments of functions"""
        if not func_doc:
            return ''

        func_doc = func_doc.replace('  ', '')
        if '\n' in func_doc:
            return func_doc.split('\n')[1]
        else:
            return func_doc

    def get_func_info(self, model, attr_name: str):
        """
        获取函数的参数信息,Get function parameters
        @param model: 插件类
        @param attr_name:属性名称，方法名称
        @return:参数信息，返回值信息，注释信息
        """
        parameters_info = []
        sig = inspect.signature(getattr(model, attr_name))
        parameters = sig.parameters
        return_annotation = sig.return_annotation
        arg_keys = tuple(arg for arg in parameters.keys() if arg != 'self')
        for arg_name in arg_keys:
            try:
                parameters_info = self.parse_pydantic_annotation(parameters[arg_name].annotation, True)
            except AttributeError:
                parameters_info = self.parse_common_annotation(parameters)

        try:
            return_annotation = self.parse_pydantic_annotation(return_annotation, False)
        except AttributeError:
            annotation = '' if "empty" in repr(return_annotation) else str(return_annotation)
            return_annotation = [
                {'name': '', 'title': '', 'type': annotation, 'default': '', 'required': True, 'desc': ''}
            ]
        func_doc = getattr(model, attr_name).__doc__
        return parameters_info, return_annotation, self.handle_func_doc(func_doc)

    def handle_orderlines_plugin(self, model):
        class_info = dict()
        methods = list()
        for attr in dir(model):
            if not attr.startswith('_') and callable(getattr(model, attr)) and attr not in self.exclude_method:
                parameters_info, return_annotation, func_doc = self.get_func_info(model, attr)
                method_info = {
                    'method_name': attr,
                    'method_desc': func_doc,
                    'parameters': parameters_info,
                    'return': return_annotation,
                }
                methods.append(method_info)
                if methods:
                    class_info['class_name'] = model.__name__
                    class_info['methods'] = methods
                if hasattr(model, 'version'):
                    class_info['version'] = getattr(model, 'version')
                if hasattr(model, 'title'):
                    class_info['title'] = getattr(model, 'title')
                if hasattr(model, 'category'):
                    class_info['category'] = getattr(model, 'category')
                if hasattr(model, 'icon'):
                    class_info['icon'] = getattr(model, 'icon')

        return class_info
